﻿/******************************************************************************
 *
 * @copyright:          Copyright 2011 UniSky Ltd.
 * @file:
 * @author:              kenping.liu
 * @creation:
 * @description:
 *
 * @modifications
 * date         user                description
 *
 *
 * @more-details
 *
 *****************************************************************************/
#include "u/u_io.h"
#include "u/u_logger.h"

#if defined(LINUX)
#   include <dirent.h>
#endif

U_NAMESPACE_BEGIN(u)
U_NAMESPACE_BEGIN(io)

void File::cleanup_path(std::string& path, bool uxstyle)
{
    for ( size_t i=1; i<path.size(); i++)
    {
        if ( path[i]=='\\' )
        {
            path[i] = '/';
        }
    }

    for ( size_t i=1; i<path.size(); )
    {
        if ( path[i-1]=='/' && path[i]=='/' )
        {
            path.erase(i, 1);
            continue;
        }
        i++;
    }

#if defined(WINDOWS)
    if ( !uxstyle )
    {
        for ( size_t i=1; i<path.size(); i++)
        {
            if ( path[i]=='/' )
            {
                path[i] = '\\';
            }
        }
    }
#endif
}

void File::split(PATH* rpath, const char* fullpath)
{
    memset(rpath, 0, sizeof(PATH));
    u_sprintf(rpath->dir, "%s", fullpath);
    u::io::File::cleanup_path(rpath->dir, MAX_PATH);

    for ( size_t i=(strlen(rpath->dir)-1); i>0; i-- )
    {
        if ( rpath->dir[i] == '/' || rpath->dir[i] == '\\')
        {
            memcpy(rpath->file, &rpath->dir[i+1], MAX_PATH-i-1);
            memset(&rpath->dir[i], 0, MAX_PATH-i);
            break;
        }
    }

    // mean just filename, have no dir
    if (strlen(rpath->file) == 0 )
    {
    	memset(rpath, 0, sizeof(PATH));
    	u_sprintf(rpath->file, "%s", fullpath);
    }
}

void File::split_exe(FILE_EXE* file_exe, const char* filename)
{
	memset(file_exe, 0, sizeof(FILE_EXE));
	std::string str(filename);
	u::io::File::cleanup_path(str);

	size_t pos = str.find_last_of('.');
	if ( (pos != std::string::npos) && (pos != str.size()) )
	{
		snprintf(file_exe->file, sizeof(file_exe->file), "%s", str.substr(0, pos).c_str());
		snprintf(file_exe->exe, sizeof(file_exe->exe), "%s", str.substr(pos+1).c_str());
	} else {
		snprintf(file_exe->file, sizeof(file_exe->file), "%s", str.c_str());
	}
}
bool File::exists(const char* fn)
{
#if defined(WINDOWS)
    return (TRUE==::PathFileExists(fn));
#else
    struct stat sb;
    return (0==stat(fn, &sb));
#endif
}

void File::mkdir(const char* _Dir)
{
    char dir[MAX_PATH] = {0};
    u_sprintf(dir, "%s", _Dir);
    cleanup_path(dir);
    if ( !exists(dir) )
    {
        //u::Log::trace("mkdir %s", dir);
        for ( size_t i=(strlen(dir)-2); i>0; i-- )
        {
            if ( dir[i]=='/' || dir[i]=='\\')
            {
                char parent[MAX_PATH] = {0};
                u_sprintf(parent, "%s", dir);                
                memset(&parent[i], 0, MAX_PATH-i);
                mkdir(parent);
                break;
            }
        }
#if defined(WINDOWS)
        CreateDirectory(dir, NULL);
#else
//        ::mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
        ::mkdir(dir, 0777);
#endif
    }
}

void File::deldir(const char* _Dst)
{
#if defined(WINDOWS)
    HANDLE          hFile;
    WIN32_FIND_DATA fi;

    char            pattern[MAX_PATH] = {0};
    char            path[MAX_PATH]    = {0};

    u_sprintf(pattern, "%s\\*", _Dst);
    hFile = FindFirstFile(pattern, &fi);
    if ( INVALID_HANDLE_VALUE != hFile )
    {
        do
        {
            if ( 0!=_tcscmp(fi.cFileName, ".") && 0!=_tcscmp(fi.cFileName, "..") )
            {
                u_sprintf(path, "%s\\%s", _Dst, fi.cFileName);
                if ( FILE_ATTRIBUTE_DIRECTORY & fi.dwFileAttributes )
                {
                    deldir(path);
                }
                else
                {
                    ::DeleteFile(path);
                }
            }

        }
        while ( ::FindNextFile(hFile, &fi) == TRUE );
      
      ::FindClose(hFile);
    }
    ::RemoveDirectory(_Dst);
#else
    char cmdline[MAX_PATH] = {0};
    u_sprintf(cmdline, "rm -rf %s", _Dst);
    u::Log::trace("delete dir [%s]", _Dst);
    int ret = system(cmdline);
    if (-1 == ret) {
    	perror("system");
    }
#endif
}

void File::cleanup_path(char* path, size_t capacity, bool uxstyle)
{
    std::string str(path);
    cleanup_path(str);
#if defined(MSC_2008)
    u_sprintf(path, capacity, "%s", str.c_str());
#else
    u_sprintf(path, "%s", str.c_str());
#endif
}

void File::del(const char* dst)
{
//    u::Log::trace("File::del('%s')", dst);
#if defined(WINDOWS)
    ::DeleteFile(dst);
#else
    remove(dst);
#endif
}

#if defined(WINDOWS)

DWORD File::size(const TCHAR* fn, HFILE _File)
{
    DWORD       ret     = 0;
    HFILE       file;
    OFSTRUCT    of      = {0};

    file = (HFILE_ERROR==_File)? ::OpenFile(fn, &of, OF_READ) : _File;
    if ( HFILE_ERROR!=file )
    {
        ret = GetFileSize((HANDLE)file, NULL);
        if ( HFILE_ERROR==_File )
        {
            CloseHandle((HANDLE)file);
        }
    }

    return 0;
}

bool File::copy(const char* src, const char* dst, bool mv)
{
    u::Log::trace("File::%s('%s' -> '%s')", mv? "move" : "copy", src, dst);
    PATH path = {0};
    split(&path, dst);
    mkdir(path.dir);
    ::DeleteFile(dst);
    if ( mv )
    {
        return TRUE==::MoveFile(src, dst);
    }
    else
    {
        return TRUE==::CopyFile(src, dst, FALSE);
    }
}

bool File::read(u::AutoHeap& content, const TCHAR* file)
{
    u::Log::trace("[kp] read_file: '%s'", file);
    bool        ret         = false;
    OFSTRUCT    of          = {0};
    OVERLAPPED  overlapped  = {0};
    HANDLE      handle      = NULL;
    AutoHeap    buf;
    DWORD       cnt         = 0;
    
    handle = (HANDLE)::OpenFile(file, &of, OF_READ);
    if ( HFILE_ERROR==(HFILE)handle )
    {
        u::Log::warn("[kp] OpenFile '%s' Failed: %s", file, u::get_error_msg());
        return false;
    }

    content.reset(GetFileSize(handle, NULL));
    TCHAR* ptr = content.head();
    overlapped.Offset = 0;
    while ( true )
    {
        buf.zero();
        cnt = buf.length();
        if ( !::ReadFile(handle, buf, buf.length(), &cnt, NULL) )
        {
            u::Log::warn("[kp] read_file('%s') ReadFile failed: %s", file, u::get_error_msg());
            break;
        }
        overlapped.Offset += cnt;
        CopyMemory(ptr, buf.head(), cnt);
        ptr += cnt;

        if ( cnt<buf.length() )
        {
            ret = true;
            break;
        }
    }
    CloseHandle(handle);

    return ret;
}

bool File::write(const TCHAR* fn, const char* buf, DWORD cnt)
{
    u::Log::trace("[kp] write_file(%s)", fn);

    bool ret = false;
    HANDLE hFile = NULL;
    while ( true )
    {
        OFSTRUCT of = {0};
        hFile = (HANDLE)::OpenFile(fn, &of, OF_CREATE|OF_WRITE);
        if ( HFILE_ERROR==(HFILE)hFile )
        {
            u::Log::warn("[kp] write_file('%s') OpenFile failed: %s", fn, u::get_error_msg());
            break;
        }

        DWORD written;
        if ( !::WriteFile(hFile, buf, (DWORD)cnt, &written, NULL) )
        {
            u::Log::warn("[kp] write_file('%s') WriteFile failed: %s", fn, u::get_error_msg());
            break;
        }

        ret = true;
        break;
    }
    if ( NULL!=hFile )
    {
        CloseHandle(hFile);
    }
    return ret;
}

bool File::append(const char* fn, const char* buf, DWORD cnt)
{    
    u::Log::trace("[kp] write_file(%s)", fn);

    bool ret = false;
    HANDLE hFile = NULL;
    while ( true )
    {
        OFSTRUCT of = {0};
        hFile = (HANDLE)::CreateFile(fn, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
            NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if ( HFILE_ERROR==(HFILE)hFile )
        {
            u::Log::warn("[kp] write_file('%s') OpenFile failed: %s", fn, u::get_error_msg());
            break;
        }

        DWORD written;
        SetFilePointer(hFile, 0, NULL, FILE_END);   
        if ( !::WriteFile(hFile, buf, (DWORD)cnt, &written, NULL) )
        {
            u::Log::warn("[kp] write_file('%s') WriteFile failed: %s", fn, u::get_error_msg());
            break;
        }

        ret = true;
        break;
    }
    if ( NULL!=hFile )
    {
        CloseHandle(hFile);
    }
    return ret;
}

bool File::last_change_time(U_FILE_TIME* ft, const TCHAR* file)
{
    bool ret = false;
    FILETIME    ftWrite = {0};
    OFSTRUCT    of      = {0};
    HANDLE      hfile   = (HANDLE)::OpenFile(file, &of, OF_READ);
    if ( HFILE_ERROR!=(HFILE)hfile )
    {
        ret = TRUE==::GetFileTime(hfile, NULL, NULL, &ftWrite);
        ft->high  = ftWrite.dwHighDateTime;
        ft->low   = ftWrite.dwLowDateTime;
        CloseHandle(hfile);
    }
    return ret;
};

void File::list(u::util::Str::Array& files, const char* dir)
{
    HANDLE          hFile; // Handle to file
    WIN32_FIND_DATA fi;    // File information
    char            filename[MAX_PATH] = {0};

    hFile = FindFirstFile("*", &fi);
    if ( INVALID_HANDLE_VALUE != hFile )
    {
        do
        {
            if ( !(FILE_ATTRIBUTE_DIRECTORY & fi.dwFileAttributes) )
            {
                u_sprintf(filename, "%s\\%s", dir, fi.cFileName);
                std::string fn(filename);
                cleanup_path(fn);
                files.push_back(fn);
            }
        }
        while ( ::FindNextFile(hFile, &fi) == TRUE );
      
      ::FindClose(hFile);
    }
}

#else

DWORD File::size(const TCHAR* fn, FILE* _File)
{
    DWORD ret  = 0;
    FILE* file = _File;

    file = (NULL==_File)? fopen(fn, "rb") : _File;
    if ( NULL!=file )
    {
        DWORD offset = (DWORD)ftell(file);
        fseek(file, 0, SEEK_END);
        ret = (DWORD)ftell(file);
        fseek(file, offset, SEEK_SET);

        if ( NULL==_File )
        {
            fclose(file);
        }
    }

    return ret;
}

bool File::copy(const char* src, const char* dst, bool mv)
{
    u::Log::trace("File::%s('%s' -> '%s')", mv? "move" : "copy", src, dst);

    PATH path = {{0}, {0}};
    split(&path, dst);
    mkdir(path.dir);

    bool ret = false;
    char cmd[1024];
    snprintf(cmd, sizeof(cmd), "cp -vf %s %s", src, dst);
    std::cout << cmd << std::endl;
    ret = system(cmd);
    if (ret < 0)
    {

    }

    ret = File::size(src) == File::size(dst);
#if 0
    FILE* from = fopen(src, "rb");
    FILE* to   = fopen(dst, "wb");
    if ( from!=NULL && to!=NULL )
    {
        u::AutoHeap heap(1024*1024);
        do
        {
            heap.zero();
            size_t bytes = fread(heap.head(), sizeof(char), 1024*1024, from);
            if ( bytes<1 )
            {
                break;
            }
            fwrite(heap.head(), sizeof(char), bytes, to);
        }
        while(true);
        ret = true;
    }
    if ( from!=NULL )
    {
        fclose(from);
    }
    if ( to!=NULL )
    {
        fclose(to);
    }
#endif
    if ( mv )
    {
        del(src);
    }
    return ret;
}

bool File::read(u::AutoHeap& content, const TCHAR* fn)
{
    bool ret = false;
    FILE* file = fopen(fn, "rb");
    if ( file != NULL )
    {
        DWORD sz = size(fn, file);
        content.reset(sz);

        char* ptr = content.head();
        u::AutoHeap buf(BUFFER_SIZE);
        do
        {
            buf.zero();
            size_t rd = fread(buf.head(), sizeof(char), BUFFER_SIZE, file);
            if ( rd<1 )
            {
                break;
            }
            memcpy(ptr, buf, rd);
            ptr += rd;
        }
        while (true);

        fclose(file);
        ret = true;
    }
    return ret;
}

bool File::write(const TCHAR* fn, const char* buf, DWORD cnt)
{
    bool ret = false;
    FILE* file = fopen(fn, "wb");

    if ( NULL!=file )
    {
        ret = (fwrite(buf, sizeof(char), cnt, file)==cnt);
        fclose(file);
    }
    return ret;
}

bool File::append(const char* fn, const char* buf, DWORD cnt)
{    
    bool ret = false;
    FILE* file = fopen(fn, "a+b");

    if ( NULL!=file )
    {
        fseek(file, 0, SEEK_END);
        ret = (fwrite(buf, sizeof(char), cnt, file)==cnt);
        fclose(file);
    }
    return ret;
}

bool File::last_change_time(U_FILE_TIME* ft, const TCHAR* file)
{
    bool ret = false;
    FILE_STATE fs = {0};
    if ( 0==stat(file, &fs) )
    {
        ft->low = (DWORD)fs.st_mtime;
        ret = true;
    }
    return ret;
};

void File::list(u::util::Str::Array& files, const char* dir)
{
    u::Log::trace("File::list('%s')", dir);

    char          filename[MAX_PATH] = {0};
    struct dirent *ep;
    DIR           *dp = opendir(dir);
    if ( dp != NULL )
    {
        while ( (ep = readdir(dp)) != NULL )
        {
            u_sprintf(filename, "%s/%s", dir, ep->d_name);
            std::string fn(filename);
            cleanup_path(fn);
            files.push_back(fn);
        }
        closedir(dp);
    }
}
#endif


U_NAMESPACE_END(io)
U_NAMESPACE_END(u)
